package workflow

import (
	"fmt"
	"log"

	"mig/pkg/comparer"
	"mig/pkg/data"
	"mig/pkg/migrator"
	"mig/pkg/reader"
	"mig/pkg/store"
	"mig/pkg/types"
	"mig/pkg/utils"
)

type workerResult struct {
	CompareResult *types.CompareResult
}

func (w *Workflow) tryFetchIntegrityInfo(database data.Database, chunkSize int64, maxWorkers int) *types.IntegrityInfo {
	rd := reader.NewDefaultReader(database, chunkSize, maxWorkers)
	tgtInfo, err := rd.Read(types.AlgorithmDigest)
	if err != nil {
		log.Printf("failed to fetch digest tree: %v", err)
		return nil
	}
	return tgtInfo
}

func (w *Workflow) worker(task *Task) (*workerResult, error) {
	cfg := task.load()
	fmt.Println(utils.FormatJSONString(cfg))
	store := store.NewDigestStore(cfg.Store)
	ds := data.NewDatabase(cfg, true)
	dt := data.NewDatabase(cfg, false)

	var (
		unequalChunks []types.UnequalChunk
		srcInfo       *types.IntegrityInfo
		tgtInfo       *types.IntegrityInfo
	)

	if cfg.ReadBeforeMigrate {
		srcInfo = w.tryFetchIntegrityInfo(ds, cfg.ChunkSize, cfg.MaxWorkers)
		tgtInfo = w.tryFetchIntegrityInfo(dt, cfg.ChunkSize, cfg.MaxWorkers)

		if !srcInfo.Empty() && !tgtInfo.Empty() {
			result := tgtInfo.Compare(srcInfo)
			fmt.Println("result:")
			result.Print()

			if len(result.UnequalChunks) == 0 {
				log.Printf("No need to migrate")
				return &workerResult{CompareResult: result}, nil
			}
			unequalChunks = result.UnequalChunks
		}
	}
	log.Printf("unequalChunks: %d", len(unequalChunks))

	for _, initSQL := range cfg.TargetDatabase.InitSQL {
		if err := dt.Execute(initSQL); err != nil {
			return nil, err
		}
	}

	var (
		result     *types.CompareResult
		resultInfo *types.IntegrityInfo
		err        error
	)
	m := migrator.NewMigrator(cfg.Algorithm, cfg.ChunkSize, cfg.MaxWorkers)
	utils.ReportExecuteTime("Migrate", func() {
		resultInfo, err = m.Migrate(ds, dt, unequalChunks)
	})
	if err != nil {
		log.Printf("failed to migrate: %v", err)
		return nil, err
	}
	if srcInfo.Empty() {
		srcInfo = resultInfo
	}

	if srcInfo != nil {
		err = store.StoreDigestTree(ds.Identifier(), srcInfo.GetDigestTree())
		if err != nil {
			return nil, err
		}
	}

	for _, postMigrateSQL := range cfg.TargetDatabase.PostMigrateSQL {
		if err = dt.Execute(postMigrateSQL); err != nil {
			return nil, err
		}
	}

	c := comparer.NewComparer(cfg.Algorithm, cfg.ChunkSize, cfg.MaxWorkers)
	utils.ReportExecuteTime("Compare", func() {
		result, err = c.Compare(ds, dt, srcInfo)
	})
	if err != nil {
		return nil, err
	}

	err = store.StoreDigestTree(dt.Identifier(), result.DigestTree)
	if err != nil {
		return nil, err
	}

	result.Print()
	return &workerResult{CompareResult: result}, nil
}
